# Create your views here.
from rest_framework import viewsets
from .models import Inform, InformRead
from .serializers import InformSerializer, ReadInformSerializer
from django.db.models import Q
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView
from django.db.models import Prefetch
"""
    视图集
"""

class InformViewSet(viewsets.ModelViewSet):
    # 自动实现 CRUD功能
    queryset = Inform.objects.all()
    serializer_class = InformSerializer

    # 通知列表
    # 1. inform.public = True
    # 2. inform.departments包含了用户所在的部门
    # 3. inform.author = request.user
    def get_queryset(self):
        # 如果多个条件的  并查，那么就需要用到Q函数
        # 提前提出  read 读者 数据    可以减少  sql语句的查询
        # 如果多个条件的并查，那么就需要用到Q函数
        """
        self.queryset：表示默认情况下视图将操作的查询集，即 Inform 模型的所有实例
        select_related('author')：使用 Django 的 select_related 函数来减少 SQL 查询的数量。它会跟随 author 字段的外键，在同一个查询中获取相关的 author 对象，这样在后续的代码中就不需要再次查询数据库来获取 author 信息。
            prefetch_related(Prefetch("reads", queryset=InformRead.objects.filter(user_id=self.request.user.uid)), 'departments')：
                使用 Django 的 prefetch_related 函数来减少 SQL 查询的数量。它会预取两个关系：一个是通过名为 reads 的字段的所有阅读记录（InformRead 对象），并且通过 Prefetch 对象指定只预取当前用户的阅读记录。另一个是预取 departments 字段关系。
            filter(Q(public=True) | Q(departments=self.request.user.department) | Q(author=self.request.user))：
                使用 Django 的 Q 对象来构建一个查询，检索满足以下任何一个条件的 Inform 实例：通知是公开的（public=True）、通知所属的部门是当前用户所在的部门（departments=self.request.user.department）、通知的作者是当前用户（author=self.request.user）。
         distinct()：确保返回的查询集是唯一的，避免重复的 Inform 实例。
        :return:
        """
        queryset = self.queryset.select_related('author').prefetch_related(
            Prefetch("reads", queryset=InformRead.objects.filter(user_id=self.request.user.uid)), 'departments').filter(
            Q(public=True) | Q(departments=self.request.user.department) | Q(author=self.request.user)).distinct()
        # for inform in queryset:
        # inform.is_read = InformRead.objects.filter(inform=inform,user=self.request.user).exsits()

        # 返回查询集，这个查询集经过了筛选和预取处理，可以直接用于视图的 list 和 retrieve 等操作。
        return queryset

    # for inform in queryset:
    #     inform.is_read = InformRead.objects.filter(inform=inform, user=self.request.user).exsits()
    # return queryset

    def destroy(self, request, *args, **kwargs):
        # 前代码重写了 Django REST framework 中 ModelViewSet 的 destroy 方法，用于处理删除通知（Inform）对象的请求。
        #destroy 方法首先调用 get_object() 方法获取要删除的 Inform 对象实例，然后检查该实例的 author 的 uid 是否与当前请求的用户 uid 相同。
        """
        如果两者相同，说明当前用户是该通知的作者，有权限删除该通知，因此调用 perform_destroy(instance) 方法执行删除操作，并返回 204 NO CONTENT 状态码，表示删除成功。
        如果实例的 author 的 uid 与当前请求的用户 uid 不相同，则返回 401 UNAUTHORIZED 状态码，表示当前用户没有权限删除该通知。

        这包括 self.queryset，self.serializer_class，以及像 get_object() 和 perform_destroy() 这样的方法。
        get_object() 方法的作用是获取当前请求所对应的对象实例。这个对象实例通常是基于 URL 中的主键值来获取的。
            例如，如果你的 URL 模式是 /inform/{id}/，那么 {id} 的值从 URL 中捕获，get_object() 方法使用这个 id 值来查找并返回对应的 Inform 对象实例。
            这个对象实例随后可以被用于 retrieve(), update(), destroy() 等方法。

        perform_destroy() 方法是 ModelViewSet 中的一个内置方法，它用于执行对象的删除操作。
            它通常在你想要删除对象时被调用。当你重写 destroy() 方法时，你可以在执行实际的删除操作之前或之后添加额外的逻辑。
            在你提供的重写的 destroy() 方法中，perform_destroy(instance) 实际上是执行了删除 Inform 对象的操作，其中 instance 是通过 get_object() 获取的对象实例。
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        instance = self.get_object()
        if instance.author.uid == request.user.uid:
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT) # 返回 204 NO CONTENT 状态码，表示删除成功。
        else:
            return Response(status=status.HTTP_401_UNAUTHORIZED) # 返回 401 UNAUTHORIZED 状态码，表示当前用户没有权限删除该通知。

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object() # 这个方法调用内部的 get_object() 函数，它根据传入的请求和 URL 参数来确定要检索的 Inform 对象。这个对象是 Django ORM 的模型实例。
        serializer = self.get_serializer(instance)# 这行代码使用了视图集的 get_serializer() 方法。这个方法返回对应的序列化器类的实例，传递给它的是 Inform 对象实例。序列化器负责将 Inform 对象转换为 JSON 或其他格式的序列化数据，以便在 HTTP 响应中使用。
        data = serializer.data # 这是序列化器实例的一个属性，包含了 Inform 对象实例的序列化表示。这个数据结构通常会包括模型字段的值，可能还有一些额外的元数据，具体取决于序列化器的定义。
        data['read_count'] = InformRead.objects.filter(inform_id=instance.id).count() # 这行代码计算了 InformRead 模型中与当前 Inform 对象实例相关的记录数量，并将这个数量作为 read_count 键值对添加到 data 字典中。这通常用于展示通知的阅读统计信息。
        return Response(data=data) # 最后，retrieve 方法使用 Django REST framework 的 Response 类来构建 HTTP 响应。data 参数包含了 Inform 对象的序列化数据以及额外添加的 read_count 信息。这个响应对象将返回给 API 的调用者。



class ReadInformView(APIView):
    def post(self, request):
        # 通知的id
        serializer = ReadInformSerializer(data=request.data) # 初始化一个名为 ReadInformSerializer 序列化器，并使用请求数据进行填充。ReadInformSerializer 类应该是在
        if serializer.is_valid(): # 检查请求数据是否通过验证。如果通过，继续后续操作。
            inform_pk = serializer.validated_data.get('inform_pk') # 从通过验证的数据中提取 inform_pk，它可能代表通知的主键或标识符。
            if InformRead.objects.filter(inform_id=inform_pk, user_id=request.user.uid).exists(): # 检查当前用户是否已经阅读过具有给定 inform_pk 的通知。这里假设 InformRead 模型中有 inform_id 和 user_id 字段来记录哪个用户阅读了哪个通知。
                return Response() #  如果用户已经阅读过该通知，直接返回一个空的响应，表示没有进行任何实质性操作。
            else:
                try:
                    InformRead.objects.create(inform_id=inform_pk, user_id=request.user.uid) # 如果用户没有阅读过该通知，则尝试在 InformRead 模型中为该用户和通知创建一条新的记录。这通常用于标记用户已读。
                except Exception as e:
                    print(e) # 在创建记录的过程中，如果发生任何错误（例如数据库错误或违反约束），则打印错误信息，并返回一个 400 Bad Request 响应，指示请求无效。
                    return Response(status=status.HTTP_400_BAD_REQUEST) # 这样可以向客户端传递错误信息，以便进行调试或错误处理。
                return Response() # 如果没有发生错误，则创建记录成功，并且返回一个空的响应。在实际应用中，这里可能需要返回一些有用的信息，例如创建的记录的ID或者确认信息。
        else:
            # 如果序列化器数据无效，将错误信息序列化为JSON格式，并返回一个 400 Bad Request 响应，以便客户端了解请求中的错误。
            return Response(data={'detail': list(serializer.errors.values())[0][0]}, status=status.HTTP_400_BAD_REQUEST)
